package cn.relx.basic.service.impl;

import cn.relx.basic.config.BussinessException;
import cn.relx.basic.constant.BaseConstans;
import cn.relx.basic.jwt.JwtUtils;
import cn.relx.basic.jwt.RsaUtils;
import cn.relx.basic.jwt.UserInfo;
import cn.relx.basic.service.LoginService;
import cn.relx.basic.util.*;
import cn.relx.basic.vo.LoginVo;
import cn.relx.basic.vo.WechatVo;
import cn.relx.system.domain.Menu;
import cn.relx.system.mapper.MenuMapper;
import cn.relx.system.mapper.PermissionMapper;
import cn.relx.user.domain.Logininfo;
import cn.relx.user.domain.User;
import cn.relx.user.domain.Wxuser;
import cn.relx.user.mapper.LogininfoMapper;
import cn.relx.user.mapper.UserMapper;
import cn.relx.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
public class LoginServiceImpl implements LoginService {
    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxuserMapper wxuserMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private PermissionMapper permissionMapper;
    @Value("${jwt.rsa.pri}")
    private String privateRsaKey;

    @Override
    @Transactional
    public HashMap<String, Object> account(LoginVo loginVo) {
        //判断账号密码是否为空
        if(StringUtils.isEmpty(loginVo.getPassword())
                || StringUtils.isEmpty(loginVo.getUsername())){
            throw new BussinessException("账号密码不能为空！");
        }
        //根据账号和查询logininfo
        Logininfo logininfo = logininfoMapper.findByUsernameAndType(loginVo);
        if(logininfo == null){
            throw new BussinessException("账号不存在，请重新输入!");
        }
        //判断加盐加密后的密码是否一致
        String passwordTmp = MD5Utils.encrypByMd5(loginVo.getPassword()+logininfo.getSalt());
        if(!logininfo.getPassword().equals(passwordTmp)){
            throw new BussinessException("账号密码错误，请重新输入！");
        }
        PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource(privateRsaKey).getFile());
        HashMap<String,Object> map = new HashMap<String,Object>();
        UserInfo userInfo = new UserInfo();
        userInfo.setLogininfo(logininfo);
        if(0 == logininfo.getType()){
            // 如果是后端 那么我们需要将权限放到token中
            // 菜单权限
            List<String> permissions = permissionMapper.findByLogininfoId(logininfo.getId());
            // 按钮权限
            List<Menu> menus = menuMapper.findByLogininfoId(logininfo.getId());
            userInfo.setPermissions(permissions);
            userInfo.setMenus(menus);
            map.put("permissions", permissions);
            map.put("menus", menus);
        }
        String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 30);
//        //生成uuid做为token存入到redis中设置有效期30分钟
//        String token = UUID.randomUUID().toString();
//        redisTemplate.opsForValue().set(
//                token,
//                logininfo,
//                30,
//                TimeUnit.MINUTES
//        );
        //将用户敏感信息置空
        logininfo.setPassword("");
        logininfo.setSalt("");
        logininfo.setPhone("");
//        HashMap<String,Object> map = new HashMap<>();
        map.put("token",token);
        map.put("logininfo",logininfo);
        return map;
    }

    @Override
    public AjaxResult weChat(WechatVo wechatVo) {
        String code = wechatVo.getCode();
        //判断参数是否为空
        if(StringUtils.isBlank(wechatVo.getCode())){
            throw new BussinessException("参数为空，请重新传递！");
        }
        //拼接请求地址，获取token和openid
        String tokenUrl = String.format(BaseConstans.wechatConstant.TOKEN_URL,
                BaseConstans.wechatConstant.APPID,
                BaseConstans.wechatConstant.SECRET,
                code);
        //发起请求获取json字符串
        String httpGet = HttpUtil.httpGet(tokenUrl);
        //将字符串json格式转换为json对象，获取到token和openid
        JSONObject jsonObject = JSONObject.parseObject(httpGet);
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        //根据openid查询t_wxuser
        Wxuser wxuser = wxuserMapper.findByOpenid(openid);
        //判断微信用户信息是否存在
        if(wxuser != null && wxuser.getUserId() != null){
            //wx用户存在并且已经绑定了账号，直接登录
            //根据wxuser中的userid查询logininfo信息
            Logininfo logininfo = logininfoMapper.findByUserId(wxuser.getUserId());
            PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource(privateRsaKey).getFile());
            HashMap<String,Object> map = new HashMap<String,Object>();
            UserInfo userInfo = new UserInfo();
            userInfo.setLogininfo(logininfo);
            if(0 == logininfo.getType()){
                // 如果是后端 那么我们需要将权限放到token中
                // 菜单权限
                List<String> permissions = permissionMapper.findByLogininfoId(logininfo.getId());
                // 按钮权限
                List<Menu> menus = menuMapper.findByLogininfoId(logininfo.getId());
                userInfo.setPermissions(permissions);
                userInfo.setMenus(menus);
                map.put("permissions", permissions);
                map.put("menus", menus);
            }
            String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 30);
//            //生成uuid做为token存入到redis中过期时间30分钟
//            String token = UUID.randomUUID().toString();
//            redisTemplate.opsForValue().set(
//                    token,
//                    logininfo,
//                    30,
//                    TimeUnit.MINUTES
//            );
            //将用户置空
            logininfo.setPassword("");
            logininfo.setSalt("");
            //设置token和用户信息到map中
//            HashMap<String,Object> hashMap = new HashMap<>();
            map.put("token",token);
            map.put("logininfo",logininfo);
            return AjaxResult.me().success().setResultObj(map);
        }
        // 6.2.不存在告诉前端用户跳转到账号绑定页面，传递参数到前端，因为我们在下个接口需要使用
        StringBuffer sbr = new StringBuffer()
                .append("?accessToken=")
                .append(access_token)
                .append("&openId=").append(openid);
         return AjaxResult.me().setSuccess(false).setResultObj(sbr);
    }

    @Override
    @Transactional
    public HashMap<String, Object> binder(WechatVo wechatVo) {
        // 1.获取数据
        String phone = wechatVo.getPhone();
        String verifyCode = wechatVo.getVerifyCode();
        String accessToken = wechatVo.getAccessToken();
        String openId = wechatVo.getOpenId();
        // 2.参数为空判断
        if(StringUtils.isBlank(phone) ||
                StringUtils.isBlank(verifyCode)||
                StringUtils.isBlank(accessToken)||
                StringUtils.isBlank(openId)){
            throw new BussinessException("参数不能为空，请填入参数！");
        }
        // 3.判断短信验证码是否正确
        isVerifyCode(wechatVo);
        // 4.根据电话查询t_user表
        User user = userMapper.findByPhone(phone);
        // 5.外层创建Logininfo对象，在if中赋值，用于等会儿最后返回给前端
        Logininfo logininfo = new Logininfo();
        // 6.判断user用户是否存在，不存在帮助用户创建，存在则查询出logininfo信息
        if(user == null){
            // 查询不到用户说明此微信用户之前没有使用此手机号创建过账号，那么我们需要给此微信用户创建user对象&logininfo对象
            // 1.初始化user对象，并保存
            user = initUser(wechatVo);
            userMapper.add(user);
            // 2.根据User对象创建logininfo对象
            logininfo = userLogininfo(user);
            // 3.保存logininfo信息
            logininfoMapper.add(logininfo);
            // 4.将logininfo主键信息设置到user中
            user.setLogininfoId(logininfo.getId());
            // 5.更新user对象信息
            userMapper.update(user);
        }else {
            // 查询的到用户说明此微信用户之前使用此手机号创建过账号，那么我们查询出此用户的logininfo信息
            logininfo = logininfoMapper.findByLogininfoId(user.getId());
        }
        // 7.根据accessToken+openId获取微信个人信息
        String wxUserUrl = BaseConstans.wechatConstant.USER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId);
        String wxUserJsonStr = HttpUtil.httpGet(wxUserUrl);
        // 因为响应的字段名称与t_wxuser表字段名称一致，所以可以直接使用JsonObject对象的parseObject方法，根据相同的字段名称赋值创建对象
        Wxuser wxuser = JSONObject.parseObject(wxUserJsonStr, Wxuser.class);
        // 8.在Wxuser对象中保存user对象Id
        wxuser.setUserId(user.getId());
        // 9.保存微信用户信息
        wxuserMapper.add(wxuser);

        PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource(privateRsaKey).getFile());
        HashMap<String,Object> map = new HashMap<String,Object>();
        UserInfo userInfo = new UserInfo();
        userInfo.setLogininfo(logininfo);
        if(0 == logininfo.getType()){
            // 如果是后端 那么我们需要将权限放到token中
            // 菜单权限
            List<String> permissions = permissionMapper.findByLogininfoId(logininfo.getId());
            // 按钮权限
            List<Menu> menus = menuMapper.findByLogininfoId(logininfo.getId());
            userInfo.setPermissions(permissions);
            userInfo.setMenus(menus);
            map.put("permissions", permissions);
            map.put("menus", menus);
        }
        String token = JwtUtils.generateTokenExpireInMinutes(userInfo, privateKey, 30);
//        // 10.生成token保存到redis中，并返回前端
//        // 生成UUID作为token存入到redis中设置有效期30分钟
//        String token = UUID.randomUUID().toString();
//        redisTemplate.opsForValue().set(
//                token,
//                logininfo,
//                30,
//                TimeUnit.MINUTES
//        );
        // 将用户敏感信息置空
        logininfo.setPassword("");
        logininfo.setSalt("");
        // 并设置token和用户信息到map中
//        HashMap<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("logininfo", logininfo);
        return map;
    }

    private User initUser(WechatVo wechatVo){
        String phone = wechatVo.getPhone();
        User user = new User();
        //设置帐号名称和电话
        user.setUsername(wechatVo.getPhone());
        user.setPhone(wechatVo.getPhone());
        //设置用户的初始化密码为123465，发送短信告知用户初始化密码
        String initPwd = "123456";
        SendMsgUtil.sendMsg(phone,"亲爱的用户您好，欢迎注册，您的初始化密码为："+initPwd+",请尽快修改密码");
        //设置盐值和加密后的密码
        String salt = StrUtils.getComplexRandomString(32);
        String md5 = MD5Utils.encrypByMd5(initPwd + salt);
        user.setSalt(salt);
        user.setPassword(md5);
        return user;
    }

    private Logininfo userLogininfo(User user){
        //利用工具类拷贝数据到logininfo
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user,logininfo);
        logininfo.setType(1);
        return logininfo;
    }

    public void isVerifyCode(WechatVo wechatVo){
        String smsCodeKey = BaseConstans.VerifyCodeConstant.BUSINESS_BINDER_PREFIX + wechatVo.getPhone();
        Object smsCodeValueTmp = redisTemplate.opsForValue().get(smsCodeKey);
        if(smsCodeValueTmp == null){
            throw new BussinessException("验证码已过期，请重新输入!");
        }
        String[] split = smsCodeValueTmp.toString().split(":");
        String smsCode = split[0];
        if(!smsCode.equalsIgnoreCase(wechatVo.getVerifyCode())){
            throw new BussinessException("验证码错误，请重新输入!");
        }

    }
}
